﻿/* 2013/12/28 */
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Drawing;

namespace Cosmos.Windows.Forms
{
    public partial class MovableTabControl : TabControl
    {
        private int _clickedTabIndex;

        private TabControlInsertionMark _insertionMark;

        private bool _allowEmpty;

        public MovableTabControl()
        {
            InitializeComponent();

            _clickedTabIndex = -1;
            _insertionMark = new TabControlInsertionMark(this);
            _allowEmpty = true;
        }

        public MovableTabControl(IContainer container)
        {
            container.Add(this);

            InitializeComponent();

            _clickedTabIndex = -1;
            _insertionMark = new TabControlInsertionMark(this);
            _allowEmpty = true;
        }

        /// <summary>
        /// Retrieves the tab that is located at the specified coordinates.
        /// </summary>
        /// <param name="position">A Point that contains the coordinates where you want to look for a tab.</param>
        /// <returns>The index of the tab that is located at the specified point.</returns>
        /// <remarks>If there is no tab at the specified point, the GetTabAtPosition method returns null.</remarks>
        private int GetTabAtPosition(Point position)
        {
            for (int i = 0; i < this.TabCount; i++)
            {
                Rectangle tabBound = this.GetTabRect(i);

                if (tabBound.Contains(position))
                {
                    return i;
                }
            }
            return -1;
        }

        private void MovableTabControl_DragDrop(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent(typeof(TabPage)))
                return;

            if (!_insertionMark.InTabControl)
                return;


            int droppedIndex = _insertionMark.Index;
            if (_insertionMark.Index == -1)
                droppedIndex = this.TabPages.Count;

            int draggedIndex = (int)e.Data.GetData(typeof(Int32));
            TabPage draggedTabPage = (TabPage)e.Data.GetData(typeof(TabPage));
            TabControl draggedTabControl = null;

            if (this == draggedTabPage.Parent)
            {
                // In the same tab control.
                if (_insertionMark.InsertAfterTab)
                {
                    if (draggedIndex > droppedIndex)
                        droppedIndex++;
                }
                else
                {
                    if (draggedIndex < droppedIndex)
                        droppedIndex--;
                }

                draggedTabControl = this;
            }
            else
            {
                // Move from another tab control
                if (_insertionMark.InsertAfterTab)
                {
                    droppedIndex++;
                }

                draggedTabControl = ((TabControl)draggedTabPage.Parent);
            }

            if (draggedTabControl.TabCount == 1)
            {
                if (draggedTabControl is MovableTabControl)
                {
                    if (!((MovableTabControl)draggedTabControl).AllowEmpty)
                    {
                        return;
                    }
                }
            }

            draggedTabControl.TabPages.Remove(draggedTabPage);
            this.TabPages.Insert(droppedIndex, draggedTabPage);
            this.SelectedIndex = droppedIndex;
        }

        private void MovableTabControl_DragOver(object sender, DragEventArgs e)
        {
            if (!e.Data.GetDataPresent(typeof(TabPage)))
            {
                e.Effect = DragDropEffects.None;
                return;
            }

            _insertionMark.InsertAtScreenPosition(new Point(e.X, e.Y));
            if (_insertionMark.InTabControl)
            {
                e.Effect = e.AllowedEffect;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }

        private void MovableTabControl_MouseDown(object sender, MouseEventArgs e)
        {
            _clickedTabIndex = GetTabAtPosition(e.Location);
        }

        private void MovableTabControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left && _clickedTabIndex != -1)
            {
                DataObject dragdropData = new DataObject();
                dragdropData.SetData(_clickedTabIndex);
                dragdropData.SetData(this.TabPages[_clickedTabIndex]);
                DoDragDrop(dragdropData, DragDropEffects.Move);
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the tab page in this tab control
        /// can be moved when it is the only one page.
        /// </summary>
        [DefaultValue(true)]
        [Category("Behavior")]
        [Description("Indicates whether the tab page in this tab control can be moved when it is the only one page.")]
        public bool AllowEmpty
        {
            get
            {
                return _allowEmpty;
            }

            set
            {
                _allowEmpty = value;
            }
        }
    }
}
